#include <Precomp.h>
#include "defs.h"
#include "Application.h"
#include "ClientListener.h"
#include "ServerConnection.h"
#include "BaseGameState.h"
#include "GameStateGetServerName.h"
#include "GameStateLogin.h"
#include "GameStateCreateNewLogin.h"
#include "GameStateTeamAndShipSelect.h"
#include "GameStateInGame.h"
#include "GameWorld.h"
#include "GameInputManager.h"
#include "UIManager.h"
#include "SoundManager.h"

#include <AVUIDispatcher.h>
#include <AVUIInputManager.h>
#include <AVUITextBlock.h>
#include <AVUIKeyInterop.h>
#include <D3DUIRenderer.h>
#include <D3D9.h>

using namespace Ogre;
using namespace AVUI;

extern bool g_isDeviceLost;

CNetrekClientListener::CNetrekClientListener(CNetrekClientApplication* pApp) :
    m_pSceneManager(NULL),
    m_pCamera(NULL),
    m_pApp(pApp),
    m_pGameState(NULL),
    m_pInputManager(NULL),
    m_eStateCur(eNetrekClientState_Invalid),
    m_eStatePending(eNetrekClientState_Invalid),
    m_pServerConnection(NULL)
{
}

class UIRenderQueueListener : public Ogre::RenderQueueListener
{
public:
    UIRenderQueueListener(CNetrekClientApplication* pApp) : m_pApp(pApp) { }
    void renderQueueStarted(Ogre::uint8, const Ogre::String&, 
    										 bool&)
    {
    }
       
    void renderQueueEnded(Ogre::uint8 id, const Ogre::String&, bool&)
    {

        if (id == Ogre::RENDER_QUEUE_OVERLAY && Ogre::Root::getSingletonPtr()->getRenderSystem()->_getViewport()->getOverlaysEnabled())
        {
            static bool uiRender = true;

            if(uiRender)
            {
                m_pApp->GetUIRenderer()->Render();
            }
        }
    }

private:
    CNetrekClientApplication* m_pApp;
};


void CNetrekClientListener::Initialize()
{
    m_pSceneManager = m_pApp->GetRoot()->getSceneManager("Default");

    m_pSceneManager->addRenderQueueListener(new UIRenderQueueListener(GetApp()));

    GetApp()->GetSoundManager()->PlaySoundStreaming(eStreamingSoundType_MusicDefault, ePlaySoundFlags_None);

    m_pCamera = m_pSceneManager->createCamera("WholeSurfaceUICamera");

    // Create one viewport, entire window
    Viewport* vp = m_pApp->GetRenderWindow()->addViewport(m_pCamera, 5000);
    vp->setClearEveryFrame(false);

    // Alter the camera aspect ratio to match the viewport
    m_pCamera->setAspectRatio(
        Real(vp->getActualWidth()) / Real(vp->getActualHeight())); 

    m_pInputManager = new CInputManager();
    if(!m_pInputManager->FInitialize())
    {
        assert(false); // I have no way to handle this.
    }

    m_pUIManager = new CUIManager(m_pApp->GetSoundManager());

    m_pGameWorld = new CGameWorld(this);

    m_pServerConnection = new CServerConnection();

    MoveToNewState(eNetrekClientState_GetServerName);

}

void CNetrekClientListener::Cleanup()
{
    delete m_pServerConnection;
    m_pServerConnection = NULL;

    delete m_pGameWorld;
    m_pGameWorld = NULL;

    delete m_pUIManager;
    m_pUIManager = NULL;

    m_pInputManager->Cleanup();
    delete m_pInputManager;
    m_pInputManager = NULL;
}

bool CNetrekClientListener::frameStarted(const FrameEvent& evt)
{
    m_flTimeSinceLastFrame = evt.timeSinceLastFrame;

    if(m_pApp->GetRenderWindow()->isClosed())
    {
        MoveToNewState(eNetrekClientState_Quit);
    }

    if(m_pInputManager != NULL)
    {
        m_pInputManager->Update(this);
    }

    if(GetApp()->GetSoundManager() != NULL)
    {
        GetApp()->GetSoundManager()->Update();
    }

    if(m_pGameState != NULL)
    {
        m_pGameState->FrameStart();
    }

    /*
#ifdef DEBUG
    CDebugHelper::Update();

    float lastFPS, avgFPS, bestFPS, worstFPS;
    m_pApp->GetRenderWindow()->getStatistics(lastFPS, avgFPS, bestFPS, worstFPS);

    WCHAR wzText[80];

    swprintf_s(wzText, L"FPS: %f", lastFPS, 80);

    ((TextBlock*)m_pApp->GetUIApplication()->get_Overlay()->FindName(L"FrameRate"))->set_Text(object_allocate<AVUI::String>(wzText));

    swprintf_s(wzText, L"Render: %d", m_pApp->GetNewUISystem()->get_RenderCount(), 80);

    ((TextBlock*)m_pApp->GetUIApplication()->get_Overlay()->FindName(L"RenderCount"))->set_Text(object_allocate<AVUI::String>(wzText));
#endif
    */

    m_pApp->GetUIApplication()->set_PresentationSourceSize(Size((float)m_pApp->GetRenderWindow()->getWidth(), (float)m_pApp->GetRenderWindow()->getHeight()));
    m_pApp->GetUIApplication()->AddDirtyRect(AVUI::Rect(0, 0, (float)m_pApp->GetRenderWindow()->getWidth(), (float)m_pApp->GetRenderWindow()->getHeight()));
    m_pApp->GetUIApplication()->Update(m_flTimeSinceLastFrame);

    IDirect3DDevice9* pDevice;

    m_pApp->GetRenderWindow()->getCustomAttribute("D3DDEVICE",&pDevice);
    pDevice->SetVertexShader(NULL);
    pDevice->SetPixelShader(NULL);

    return true;
}

bool CNetrekClientListener::frameEnded(const FrameEvent&) 
{   
    if(m_eStatePending != m_eStateCur)
    {
        TransitionToNewState();
    }

    if(m_pGameState != NULL)
    {
        m_pGameState->FrameEnd();
    }

    if(m_eStatePending == eNetrekClientState_Quit)
    {
        AVUI::Application::get_Current()->set_MainWindow(NULL);
        return false;
    }

    return true; 
}

void CNetrekClientListener::OnKeyboardAcquired()
{
    if(m_pGameState != NULL)
    {
        m_pGameState->OnKeyboardAcquired();
    }
}

void CNetrekClientListener::OnMouseAcquired()
{
    if(m_pGameState != NULL)
    {
        m_pGameState->OnMouseAcquired();
    }
}

void CNetrekClientListener::MouseMoved(int dx, int dy, int dz)
{
    if(m_pGameState != NULL)
    {
        m_pGameState->MouseMoved(dx, dy, dz);
    }

    POINT pt;

    GetCursorPos(&pt);
    ScreenToClient(GetActiveWindow(), &pt);

    if(!g_isDeviceLost)
    {
        Dispatcher::get_CurrentDispatcher()->get_InputManager()->MessageMousePosition(pt.x, pt.y, 0);
    }
}

void CNetrekClientListener::MousePressed(EMouseButtonID eMouseButtonID)
{
    if(m_pGameState != NULL)
    {
        m_pGameState->MousePressed(eMouseButtonID);
    }

    if(!g_isDeviceLost)
    {
        Dispatcher::get_CurrentDispatcher()->get_InputManager()->MessageMouseButtonDown(MouseButton::Left);
    }
}

void CNetrekClientListener::MouseReleased(EMouseButtonID eMouseButtonID)
{
    if(m_pGameState != NULL)
    {
        m_pGameState->MouseReleased(eMouseButtonID);
    }

    if(!g_isDeviceLost)
    {
        Dispatcher::get_CurrentDispatcher()->get_InputManager()->MessageMouseButtonUp(MouseButton::Left);
    }
}

void CNetrekClientListener::CharEntered(WCHAR wch)
{
    if(!g_isDeviceLost)
    {
        Dispatcher::get_CurrentDispatcher()->get_InputManager()->MessageKeyChar(KeyInterop::KeyFromVirtualKey(m_vkLastDown), wch);
    }
}


void CNetrekClientListener::KeyPressed(EKeyboardButtonID eKeyboardButtonID)
{
}

void CNetrekClientListener::KeyReleased(EKeyboardButtonID eKeyboardButtonID)
{
}

void CNetrekClientListener::KeyUp(int keyCode)
{
    if(!Dispatcher::get_CurrentDispatcher()->get_InputManager()->MessageKeyUp(KeyInterop::KeyFromVirtualKey(keyCode)))
    {
        if(m_pGameState != NULL)
        {
            m_pGameState->KeyReleased((EKeyboardButtonID) keyCode);
        }
    }
}

void CNetrekClientListener::KeyDown(int keyCode)
{
    m_vkLastDown = keyCode;

    if(!Dispatcher::get_CurrentDispatcher()->get_InputManager()->MessageKeyDown(KeyInterop::KeyFromVirtualKey(keyCode)))
    {
        if(keyCode == VK_ESCAPE)
        {
//            m_pUIManager->ShowOptionsWindow();
        }
        else
        {
            if(m_pGameState != NULL)
            {
                m_pGameState->KeyPressed((EKeyboardButtonID) keyCode);
            }
        }
    }
}


void CNetrekClientListener::TransitionToNewState()
{
    if(m_pGameState != NULL)
    {
        m_pGameState->Cleanup();
        delete m_pGameState;

        m_pGameState = NULL;
    }

    switch(m_eStatePending)
    {
        case eNetrekClientState_GetServerName:
        {
            m_pGameState = new CGameStateGetServerName();
        }
        break;

        case eNetrekClientState_Login:
        {
            m_pGameState = new CGameStateLogin();
        }
        break;

        case eNetrekClientState_CreateNewLogin:
        {
            m_pGameState = new CGameStateCreateNewLogin();
        }
        break;

        case eNetrekClientState_TeamAndShipSelect:
        {
            m_pGameState = new CGameStateTeamAndShipSelect();
        }
        break;

        case eNetrekClientState_InGame:
        {
            m_pGameState = new CGameStateInGame();
        }
        break;

        case eNetrekClientState_Quit:
        {
            m_pGameState = NULL;
        }
        break;

        default:
        {
            assert(false);
        }
        break;
    }

    if(m_pGameState != NULL)
    {
        m_pGameState->Initialize(this);
    }

    m_eStateCur = m_eStatePending;
}

Ogre::Root* CNetrekClientListener::GetRoot() { return m_pApp->GetRoot(); }



